/*
 * Copyright (c) 2009-2011, okazoh_tk. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *  - Neither the name of the copyright holder nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "alkes/media/PixelFormat.h"
#include "alkes/core/Macro.h"

#define DEBUG_COLOR false

#define C2RGBA4444(color)                       \
        (((uint16_t)color.r << 8) & 0xf000) |   \
        (((uint16_t)color.g << 4) & 0x0f00) |   \
        (((uint16_t)color.b     ) & 0x00f0) |   \
        (((uint16_t)color.a >> 4) & 0x000f)

#define C2RGB565(color)                         \
        (((uint16_t)color.r << 8) & 0xf800) |   \
        (((uint16_t)color.g << 3) & 0x07E0) |   \
        (((uint16_t)color.b >> 3) & 0x001F)

namespace alkes {

static const Color RGBA8888_getPixel(const uint8_t* p)
{
    return *(const Color*)p;
}

static void RGBA8888_setPixel(uint8_t* p, const Color& color)
{
    *(Color*)p = color;
}

static void RGBA8888_fillScanline(uint8_t* p, uint16_t num, const Color& color)
{
    uint16_t i;
    for (i = 0; i < num; ++i, p += 4)
        *(Color*)p = color;
}

static const Color RGB888_getPixel(const uint8_t* p)
{
    Color color = {*p,*(p+1),*(p+2),0xff};
    return color;
}

static void RGB888_setPixel(uint8_t* p, const Color& color)
{
    *(p  ) = color.r;
    *(p+1) = color.g;
    *(p+2) = color.b;
}

static void RGB888_fillScanline(uint8_t* p, uint16_t num, const Color& color)
{
    uint16_t i;
    for (i = 0; i < num; ++i, p += 3)
    {
        *(p  ) = color.r;
        *(p+1) = color.g;
        *(p+2) = color.b;
    }
}

static const Color RGBA4444_getPixel(const uint8_t* p)
{
    uint16_t c = *p | (uint16_t)*p << 8;
    Color color = {
        ((c>>8)&0xf0) | ((c>>12)&0x0f),
        ((c>>4)&0xf0) | ((c>> 8)&0x0f),
        ((c   )&0xf0) | ((c>> 4)&0x0f),
        ((c<<4)&0xf0) | ((c    )&0x0f)
    };
    return color;
}

static void RGBA4444_setPixel(uint8_t* p, const Color& color)
{
    *(uint16_t*)p = C2RGBA4444(color);
}

static void RGBA4444_fillScanline(uint8_t* p, uint16_t num, const Color& color)
{
    uint16_t c = C2RGBA4444(color);

    uint16_t i;
    for (i = 0; i < num; ++i, p += 2)
        *(uint16_t*)p = c;
}

static const Color RGB565_getPixel(const uint8_t* p)
{
    uint16_t c = *p | (uint16_t)*p << 8;
    Color color = {
        (c>>8)&0xf8,    // 0xF800
        (c>>5)&0xfC,    // 0x07E0
        (c<<3)&0xf8,    // 0x001F
        0xff
    };
    return color;
}

static void RGB565_setPixel(uint8_t* p, const Color& color)
{
    *(uint16_t*)p = C2RGB565(color);
}

static void RGB565_fillScanline(uint8_t* p, uint16_t num, const Color& color)
{
    uint16_t c =C2RGB565(color);

    uint16_t i;
    for (i = 0; i < num; ++i, p += 2)
        *(uint16_t*)p = c;
}

static const Color A8_getPixel(const uint8_t* p)
{
    Color color = {0,0,0,*p};
    return color;
}

static void A8_setPixel(uint8_t* p, const Color& color)
{
    *p = color.a;
}

static void A8_fillScanline(uint8_t* p, uint16_t num, const Color& color)
{
    uint16_t i;
    for (i = 0; i < num; ++i, ++p)
        *p = color.a;
}


static const alkes::PixelFormatTraits s_traits_table[] = {
    // bytes, num,  (offset, bits), (offset, bits), (offset, bits), (offset, bits)

    // AL_PIXEL_FORMAT_UNKNOWN
    {   0, 0, -1,
        {
            { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}
        },
        0,
        0,
        0
    },

    // AL_PIXEL_FORMAT_RGBA8888
    {   4, 4, 3,
        {
            { 0, 8}, { 8, 8}, {16, 8}, {24, 8}
        },
        RGBA8888_getPixel,
        RGBA8888_setPixel,
        RGBA8888_fillScanline
    },

    // AL_PIXEL_FORMAT_RGBX8888
    {   4, 4, -1,
        {
            { 0, 8}, { 8, 8}, {16, 8}, {24, 8}
        },
        RGBA8888_getPixel,
        RGBA8888_setPixel,
        RGBA8888_fillScanline
    },

    // AL_PIXEL_FORMAT_RGB888
    {   3, 3, -1,
        {
            { 0, 8}, { 8, 8}, {16, 8}, { 0, 0}
        },
        RGB888_getPixel,
        RGB888_setPixel,
        RGB888_fillScanline
    },

    // AL_PIXEL_FORMAT_RGBA4444
    {   2, 4, 3,
        {
            {12, 4}, { 8, 4}, { 4, 4}, { 0, 4}
        },
        RGBA4444_getPixel,
        RGBA4444_setPixel,
        RGBA4444_fillScanline
    },

    // AL_PIXEL_FORMAT_RGBX4444
    {   2, 4, -1,
        {
            {12, 4}, { 8, 4}, { 4, 4}, { 0, 4}
        },
        RGBA4444_getPixel,
        RGBA4444_setPixel,
        RGBA4444_fillScanline
    },

    // AL_PIXEL_FORMAT_RGB565
    {   2, 3, -1,
        {
            {11, 5}, { 5, 6}, { 0, 5}, { 0, 0}
        },
        RGB565_getPixel,
        RGB565_setPixel,
        RGB565_fillScanline
    },

    // AL_PIXEL_FORMAT_A8
    {   1, 1, 0,
        {
            { 0, 8}, { 0, 0}, { 0, 0}, { 0, 0}
        },
        A8_getPixel,
        A8_setPixel,
        A8_fillScanline
    },
};

}

static alkes::GetPixelFormatTraits s_traits_func = 0;

AL_EXPORT_API const alkes::PixelFormatTraits* alkes_get_pixel_format_traits(alkes::PixelFormat format)
{
    if (!format)
    {
        LOGE("Invalid pixel format.(%d)", format);
        return 0;
    }

    if (s_traits_func)
    {
        const alkes::PixelFormatTraits* traits = s_traits_func(format);
        if (traits)
            return traits;
    }

    if (AL_LENGTH(alkes::s_traits_table) < format)
    {
        alkes_abort();
        return 0;
    }

    LOGV_IF(DEBUG_COLOR, "bytes=%d, component_num=%d, alpha_index=%d",
        alkes::s_traits_table[format].bytes,
        alkes::s_traits_table[format].component_num,
        alkes::s_traits_table[format].alpha_index);

    return &alkes::s_traits_table[format];
}

AL_EXPORT_API alkes::GetPixelFormatTraits alkes_set_pixel_format_traits_func(alkes::GetPixelFormatTraits function)
{
    alkes::GetPixelFormatTraits old_func = s_traits_func;

    s_traits_func = function;

    return old_func;
}
